function chipset( data, callback ) {
	var self = this;

	for( var i in data )
		self[i] = data[i];

	var w = config.tile.width;
	var h = config.tile.height;

	var autorows = Math.ceil( self.autotile.length / 8 );
	var autoheight = autorows * h;

	var max;

	self.display = function( target ) {
		target.width = self.tileset.width;
		target.height = self.tileset.height + autoheight;

		var ctx = target.getContext("2d");
		ctx.drawImage( self.tileset, 0, autoheight );

		for( var i = 0; i < self.autotile.length; i++ ) {
			var x = ( i % 8 ) * w;
			var y = Math.floor( i / 8 ) * h;
			ctx.drawImage( self.autotile[i][2], 0, 0, w, h, x, y, w, h );
		}
	};

	self.change = function( i ) {
		i = i-autorows*8;
		if( i >= 0) {
			var l = self.data[i]+1;
			if( l > 2 ) l = 0;
			self.data[i] = l;
		}
	};

	self.collision = function( target ) {
		var ctx = target.getContext("2d");
		var x, y;

		ctx.globalAlpha = 0.5;

		for( var i = 0; i < self.autotile.length; i++ ) {
			x = ( i % 8 ) * w;
			y = Math.floor( i / 8 ) * h;
			ctx.drawImage( graphic.symbols, self.autotile[i][1]*w, 0, w, h, x, y, w, h );
		}

		for( var i = 0; i < max; i++ ) {
			x = ( i % 8 ) * w;
			y = autoheight + Math.floor( i / 8 ) * h;
			ctx.drawImage( graphic.symbols, self.data[i]*w, 0, w, h, x, y, w, h );
		}

		ctx.globalAlpha = 1.0;
	};

	self.level = function( i ) {
		if( i < autorows * 8 ) {
			if( i < self.autotile.length ) return self.autotile[i][1];
			else return 0;
		} else {
			return self.data[i-autorows*8];
		}
	};

	self.tile = function( ctx, i, tx, ty, map ) {
		if( i < autorows * 8 ) {
			if( i < self.autotile.length )
				self.drawauto(ctx, i, tx, ty, map);
		} else {
			var i = i-autorows*8;

			var sx = ( i % 8 ) * w;
			var sy = Math.floor( i / 8 ) * h;

			var dx = tx*32;
			var dy = ty*32;

			ctx.drawImage( self.tileset, sx, sy, w, h, dx, dy, w, h );
		}
	};

	self.drawauto = function( ctx, i, tx, ty, mapdata ) {
		// check if the following directions differ from current tile
		var left   = tx > 0 && mapdata[tx][ty] != mapdata[tx-1][ty];
		var right  = tx < mapdata.length - 1 && mapdata[tx][ty] != mapdata[tx+1][ty];
		var top    = ty > 0 && mapdata[tx][ty] != mapdata[tx][ty-1];
		var bottom = ty < mapdata[0].length - 1 && mapdata[tx][ty] != mapdata[tx][ty+1];

		var base = {x: w, y: h*2};
		var image = self.autotile[i][2];

		var dx = tx*w;
		var dy = ty*h;

		if( top ) base.y -= h;
		if( bottom ) base.y += h;
		if( left ) base.x -= w;
		if( right ) base.x += w;

		if( left && right ) {
			if( bottom && top ) {
				ctx.drawImage( image, 0, 0, w, h, dx, dy, w, h );
			} else {
				ctx.drawImage( image, 0, base.y, w/2, h, dx, dy, w/2, h );
				ctx.drawImage( image, w*2.5, base.y, w/2, h, dx+w/2, dy, w/2, h );
			}
		} else if( bottom && top ) {
			ctx.drawImage( image, base.x, h, w, h/2, dx, dy, w, h/2 );
			ctx.drawImage( image, base.x, h*3.5, w, h/2, dx, dy+h/2, w, h/2 );
		} else {
			ctx.drawImage( image, base.x, base.y, w, h, dx, dy, w, h );
		}

		if( !left && !top && tx > 0 && ty > 0 && mapdata[tx][ty] != mapdata[tx-1][ty-1] )
			ctx.drawImage( image, w*2, 0, w/2, h/2, dx, dy, w/2, h/2 );
		if( !left && !bottom && tx > 0 && ty < mapdata[0].length - 1 && mapdata[tx][ty] != mapdata[tx-1][ty+1] )
			ctx.drawImage( image, w*2, h/2, w/2, h/2, dx, dy+h/2, w/2, h/2 );
		if( !right && !top && tx < mapdata.length - 1 && ty > 0 && mapdata[tx][ty] != mapdata[tx+1][ty-1] )
			ctx.drawImage( image, w*2.5, 0, w/2, h/2, dx+w/2, dy, w/2, h/2 );
		if( !right && !bottom && tx < mapdata.length - 1 && ty < mapdata[0].length - 1 && mapdata[tx][ty] != mapdata[tx+1][ty+1] )
			ctx.drawImage( image, w*2.5, h/2, w/2, h/2, dx+w/2, dy+h/2, w/2, h/2 );
	};

	graphic.urls.push( [ 'chipset_'+self.id, self.tileset ] );

	for( var i = 0; i < self.autotile.length; i++ )
		graphic.urls.push( [ 'autotile_'+self.id+'_'+i, self.autotile[i][0] ] );

	graphic.load( function() {
		self.tileset = graphic['chipset_'+self.id];
		max = Math.floor( self.tileset.width/w ) * Math.floor( self.tileset.height / h );

		for( var i = 0; i < self.autotile.length; i++ )
			self.autotile[i].push( graphic['autotile_'+self.id+'_'+i] );

		if( !self.data )
			self.data = [];
		for( var i = 0; i < max; i++ )
			if( typeof self.data[i] == 'undefined' ) self.data[i] = 0;

		callback();
	});
}